#ifndef NIGHT_ARRAY_CORE
#define NIGHT_ARRAY_CORE

#include "../night.hpp"
#include "../tensor/core.hpp"
#include <algorithm>

namespace night {

namespace array {

template< size_type iSize=0, size_type jSize=0, size_type kSize=0 > struct size;

template< typename E, typename Size = typename E::shape_type, int dim = Size::dim > struct iterator;
enum iterator_type { begin_iterator, end_iterator };
template< typename E > struct iterator_wrapper;

struct array_tag {};
struct c_array_tag {};
struct zero_tag {};
struct scalar_tag {};
struct diagonal_array_tag {};

template< typename E, typename R = typename E::result_type, typename Size = typename E::shape_type > struct Expr; 
template< typename E, typename R, typename S, int dim = S::dim > struct bind_view;
template< typename E, typename R, typename S, typename Index1=void, typename Index2=void, typename Index3=void > 
struct slice_view;

template< typename T > struct scalar_view;
template< typename T, size_type iSize, size_type jSize=0, size_type kSize=0 > struct array_view;
template< typename T, size_type iSize, size_type jSize=0, size_type kSize=0 > struct pointer_view;
template< typename T, size_type iSize, size_type jSize=0, size_type kSize=0 > struct function_view;

template< size_type iSize, typename T >
inline pointer_view<T,iSize> c_array ( T * src ) { return (src); }
template< size_type iSize, size_type jSize, typename T >
inline pointer_view<T,iSize,jSize> c_array ( T * src ) { return (src); }
template< size_type iSize, size_type jSize, size_type kSize, typename T >
inline pointer_view<T,iSize,jSize,kSize> c_array ( T * src ) { return (src); }

template< size_type iSize, typename T >
inline array_view<T,iSize> c_array ( T (&src) [iSize] ) { return array_view<T,iSize> (src); }
template< size_type iSize, size_type jSize, typename T >
inline array_view<T,iSize,jSize> c_array ( T (&src) [iSize][jSize] ) { return array_view<T,iSize,jSize> (src); }
template< size_type iSize, size_type jSize, size_type kSize, typename T >
inline array_view<T,iSize,jSize,kSize> c_array ( T (&src) [iSize][jSize][kSize] ) { return array_view<T,iSize,jSize,kSize> (src); }

template< typename E, typename R, typename Size >
struct Expr : Size, detail::Base< Expr<E,R,Size> >
{
	typedef array_tag array_category;
	typedef Expr base_type;
	typedef R result_type;
	typedef Size shape_type;

	typedef typename detail::remove_cref<R>::type value_type;
	typedef value_type * pointer;
	typedef value_type & reference;
	typedef night::size_type size_type;

	Expr () : Size () {}
	Expr ( Size const & src ) : Size (src) {}

	iterator_wrapper<E const> begin () const { return iterator_wrapper<E const> ( derived<E> (this), begin_iterator ); }
	iterator_wrapper<E const> end () const { return iterator_wrapper<E const> ( derived<E> (this), end_iterator ); }

	size_type size (int n) const { return static_cast<Size const *> (this) -> get (n); }
	size_type size () const { return static_cast<Size const *> (this) -> length (); }

	Size const & shape () const { return (*this); }

	template< typename RE, typename RR > E & assign ( Expr<RE,RR,Size> const & rhs ) { 
//		std::copy( derived<RE> (rhs).begin (), derived<RE> (rhs).end (), derived<E> (this)->begin () );
		for ( size_type i=0; i< this->size(0); ++i ) derived<E> (*this)[i] = derived<RE> (rhs)[i];
		return derived<E> (*this);
	}
	E & assign ( value_type const & rhs ) { 
		for ( size_type i=0; i< this->size(0); ++i ) derived<E> (*this)[i] = rhs;
		return derived<E> (*this);
	}

	typename bind_view< by_cref<E>,R,Size >::type operator [] ( size_type i ) const {
		return bind_view< by_cref<E>,R,Size >::apply ( derived<E> (*this), i );
	}

	template<int i>
	tensor::tensor_view< by_cref<E>, R, tensor::dimension< Size::template base<0>::value, index<i> > > operator [] ( index<i> ) const { 
		return derived<E> (*this);
	}

	template< typename Index1 >
	typename slice_view< by_cref<E>,R,Size,Index1 >::type operator () ( Index1 i ) const { 
		return slice_view< by_cref<E>,R,Size,Index1 >::apply ( derived<E>(*this), i );
	}
	template< typename Index1, typename Index2 >
	typename slice_view< by_cref<E>,R,Size,Index1,Index2 >::type operator () ( Index1 i, Index2 j ) const { 
		return slice_view< by_cref<E>,R,Size,Index1,Index2 >::apply ( derived<E>(*this), i, j ); 
	}
	template< typename Index1, typename Index2, typename Index3 >
	typename slice_view< by_cref<E>,R,Size,Index1,Index2,Index3 >::type operator () ( Index1 i, Index2 j, Index3 k ) const { 
		return slice_view< by_cref<E>,R,Size,Index1,Index2,Index3 >::apply ( derived<E>(*this), i, j, k);
	}

};

} // end namespace night::array

} // end namespace night

#endif
